﻿using System.Collections.Generic;
using UnityEngine;
using UnityView.Utils;

namespace UnityView
{
    public class FlowListView : AbsAdapterView<IMultipleAdapter>
    {
        public readonly int StackCount;
        public float StackSize { protected set; get; }

        protected readonly FlowStack[] FlowStacks;

        // 类型数组
        public int[] TypeCache;
        public readonly AdapterCellCache<FlowAdapterCellHolder>[] Caches;
        protected float[] SizeCache;
        protected float[] AnchorCache;
        protected int[] StackStartIndexCache;
        protected int[] StackVisibleCache;

        public FlowListView(int stackCount, int typeCount)
        {
            StackCount = stackCount;
            Caches = new AdapterCellCache<FlowAdapterCellHolder>[typeCount];
            for (int i = 0; i < typeCount; i++)
            {
                Caches[i] = new AdapterCellCache<FlowAdapterCellHolder>();
            }

            FlowStacks = new FlowStack[stackCount];
            for (int i = 0; i < stackCount; i++)
            {
                FlowStacks[i] = new FlowStack(this, i);
            }

            SizeCache = new float[ItemCount];
            AnchorCache = new float[ItemCount];
            StackStartIndexCache = new int[stackCount];
            StackVisibleCache = new int[stackCount];
        }

        public override void OnAdapterAttached(IMultipleAdapter adapter)
        {
            ItemCount = Adapter.GetCount();
            TypeCache = new int[ItemCount];
            if (SizeCache.Length < ItemCount)
            {
                float[] heightArray = new float[ItemCount];
                float[] anchorArray = new float[ItemCount];
                SizeCache.CopyTo(heightArray, 0);
                AnchorCache.CopyTo(anchorArray, 0);
                SizeCache = heightArray;
                AnchorCache = anchorArray;
            }

            float anchor = 0;
            for (int i = 0; i < ItemCount; i++)
            {
                TypeCache[i] = Adapter.GetItemType(i);
                float height = Adapter.GetItemSize(i);
                SizeCache[i] = height;
                AnchorCache[i] = anchor;
                anchor += height;
            }

            Generate();
            UpdateVisibleItemCount();
            Reload();
        }

        protected void Generate()
        {
            List<int>[] indexs = new List<int>[StackCount];
            List<float>[] sizes = new List<float>[StackCount];
            List<float>[] anchors = new List<float>[StackCount];
            float[] endAnchors = new float[StackCount];
            for (int i = 0; i < StackCount; i++)
            {
                float size = Orientation == Orientation.Horizontal ? Height : Width;
                float spacing = Orientation == Orientation.Horizontal ? Spacing.y : Spacing.x;
                StackSize = (size - (spacing * (StackCount + 1))) / StackCount;
                FlowStacks[i].Reset(StackSize, spacing);
                endAnchors[i] = (Orientation == Orientation.Horizontal ? Spacing.x : Spacing.y) + HeaderViewSize;
                indexs[i] = new List<int>();
                sizes[i] = new List<float>();
                anchors[i] = new List<float>();
                anchors[i].Add(endAnchors[i]);
            }

            int minAnchorIndex = 0;
            for (int i = 0; i < ItemCount; i++)
            {
                indexs[minAnchorIndex].Add(i);
                var size = Adapter.GetItemSize(i);
                sizes[minAnchorIndex].Add(size);
                var anchorList = anchors[minAnchorIndex];
                float endAnchor = size + anchorList[anchorList.Count - 1] +
                                  (Orientation == Orientation.Horizontal ? Spacing.x : Spacing.y);
                anchorList.Add(endAnchor);
                endAnchors[minAnchorIndex] = endAnchor;

                // 获取高度最小的栈
                minAnchorIndex = 0;
                endAnchor = endAnchors[0];
                for (int j = 1; j < StackCount; j++)
                {
                    if (endAnchors[j] < endAnchor)
                    {
                        minAnchorIndex = j;
                        endAnchor = endAnchors[j];
                    }
                }
            }

            for (int i = 0; i < StackCount; i++)
            {
                FlowStacks[i].IndexCache = indexs[i].ToArray();
                FlowStacks[i].SizeCache = sizes[i].ToArray();
                FlowStacks[i].AnchorCache = anchors[i].ToArray();
                FlowStacks[i].AnchorEnd = endAnchors[i];
                FlowStacks[i].ItemCount = FlowStacks[i].IndexCache.Length;
            }
        }

        private int _column;

        public int Column
        {
            set
            {
                _column = value;
                Reload(true);
            }
            get { return _column; }
        }

        // 覆写 OnScroll 事件
        protected override void OnScroll(Vector2 position)
        {
            float anchor = Orientation == Orientation.Horizontal
                ? -ContentTransform.anchoredPosition.x
                : ContentTransform.anchoredPosition.y;
            // 对每一个Stack都分别调用Reposition
            for (int i = 0; i < StackCount; i++)
            {
                FlowStack stack = FlowStacks[i];
                int index = stack.UpdateStartIndex(anchor);
                int visible = stack.UpdateVisibleItemCount(index);
                var delta = Orientation == Orientation.Horizontal
                    ? anchor + LastRepositionPosition.x
                    : anchor - LastRepositionPosition.y;
                if (StackStartIndexCache[i] != index || StackVisibleCache[i] != visible)
                {
                    StackStartIndexCache[i] = index;
                    StackVisibleCache[i] = visible;
                    stack.Reposition(delta);
                }
            }

            LastRepositionPosition = ContentTransform.anchoredPosition;
        }

        public override void UpdateVisibleItemCount()
        {
            VisibleItemCount = 0;
            for (int i = 0; i < StackCount; i++)
            {
                VisibleItemCount += FlowStacks[i].UpdateVisibleItemCount();
            }
        }

        public override Vector2 GetItemPosition(int index)
        {
            return Vector2.zero;
        }

        public override int GetItemIndex(Vector2 vector2)
        {
            int stackIndex = GetStackIndex(vector2);
            if (stackIndex < 0) return -1;
            return
                FlowStacks[stackIndex].GetItemIndex(Orientation == Orientation.Horizontal
                    ? vector2.x
                    : vector2.y + ContentTransform.anchoredPosition.y);
        }

        protected override int GetStartIndex()
        {
            if (Adapter == null || StackCount == 0) return 0;
            float anchor = Orientation == Orientation.Horizontal
                ? -ContentTransform.anchoredPosition.x
                : ContentTransform.anchoredPosition.y;
            int startIndex = FlowStacks[0].IndexCache[FlowStacks[0].UpdateStartIndex(anchor)];
            for (int i = 1; i < StackCount; i++)
            {
                int si = FlowStacks[i].IndexCache[FlowStacks[i].UpdateStartIndex(anchor)];
                if (si < startIndex) startIndex = si;
            }

            return startIndex;
        }

        public override void Reload()
        {
            ScrollRect.StopMovement();
            UpdateContentSize();
            for (int i = 0; i < FlowStacks.Length; i++)
            {
                FlowStacks[i].Reload(Orientation == Orientation.Horizontal
                    ? ContentTransform.anchoredPosition.x
                    : ContentTransform.anchoredPosition.y);
            }
        }

        protected Vector2 LastRepositionPosition = Vector2.zero;

        public override void LayoutSubviews()
        {
            float delta = Orientation == Orientation.Horizontal
                ? LastRepositionPosition.x - ContentTransform.anchoredPosition.x
                : ContentTransform.anchoredPosition.y - LastRepositionPosition.y;
            for (int i = 0; i < StackCount; i++)
            {
                FlowStacks[i].Reposition(delta);
            }
        }

        protected override Vector2 UpdateContentSize()
        {
            if (ItemCount == 0 || FlowStacks == null) return Vector2.zero;
            float max = FlowStacks[0].AnchorEnd;
            for (int i = 1; i < FlowStacks.Length; i++)
            {
                if (FlowStacks[i].AnchorEnd > max) max = FlowStacks[i].AnchorEnd;
            }

            ContentTransform.sizeDelta = Orientation == Orientation.Horizontal
                ? new Vector2(max, Height)
                : new Vector2(Width, max);
            return ContentTransform.sizeDelta;
        }

        public int GetStackIndex(Vector2 vector2)
        {
            float spacing = Orientation == Orientation.Horizontal ? Spacing.y : Spacing.x;
            float offset = (Orientation == Orientation.Horizontal ? vector2.y : vector2.x) - spacing;
            int index = Mathf.FloorToInt(offset / (StackSize + spacing));
            if (index >= StackCount) return -1;
            offset = offset.Modf(StackSize + spacing);
            if (offset > StackSize) return -1;
            return index;
        }
    }

    public class FlowStack
    {
        public readonly FlowListView ListView;
        public readonly int Index;
        public float Size;
        public float AnchorOffset;

        public FlowStack(FlowListView listView, int index)
        {
            ListView = listView;
            Index = index;
        }

        public float AnchorEnd;
        public int ItemCount;
        public int[] IndexCache;
        public float[] SizeCache;
        public float[] AnchorCache;
        public readonly LinkedList<FlowAdapterCellHolder> Items = new LinkedList<FlowAdapterCellHolder>();

        protected int StartIndex = 0;

        protected float AnchoredPosition
        {
            get
            {
                return ListView.Orientation == Orientation.Horizontal
                    ? ListView.ContentTransform.anchoredPosition.x
                    : ListView.ContentTransform.anchoredPosition.y;
            }
        }

        public int UpdateStartIndex(float position)
        {
            StartIndex = Algorithm.BinarySearchFloat(AnchorCache, position);
            if (StartIndex < 0) StartIndex = 0;
            return StartIndex;
        }

        protected int VisibleItemCount = 0;

        public int UpdateVisibleItemCount()
        {
            return UpdateVisibleItemCount(UpdateStartIndex(AnchoredPosition));
        }

        public int UpdateVisibleItemCount(int index)
        {
            if (ItemCount == 0) return 0;
            VisibleItemCount = 0;
            float height = -SizeCache[index];
            float max = ListView.Orientation == Orientation.Horizontal ? ListView.Width : ListView.Height;
            while (index < ItemCount && height < max)
            {
                height += SizeCache[index];
                index++;
                VisibleItemCount++;
            }

            return VisibleItemCount;
        }

        // 重置栈，此时下标需要重新设置
        public void Reset(float size, float spacing)
        {
            Size = size;
            AnchorOffset = Index * Size + (Index + 1) * spacing;
            AnchorEnd = ListView.Orientation == Orientation.Horizontal
                ? ListView.Spacing.x
                : ListView.Spacing.y;
        }

        public void Reload(float position)
        {
            UpdateVisibleItemCount(UpdateStartIndex(position));
            List<FlowAdapterCellHolder> cells = new List<FlowAdapterCellHolder>();
            for (int i = 0; i < VisibleItemCount; i++)
            {
                LinkedListNode<FlowAdapterCellHolder> node = Items.First;
                cells.Add(node == null ? new FlowAdapterCellHolder() : node.Value);
                Items.RemoveFirst();
            }

            foreach (var item in Items)
            {
                ListView.Caches[ListView.TypeCache[item.Index]].Enqueue(item);
            }

            Items.Clear();

            for (int i = 0; i < VisibleItemCount; i++)
            {
                var cell = cells[i];
                int index = StartIndex + i;
                int globalIndex = IndexCache[index];
                IViewLayout viewLayout = ListView.Adapter.GetConvertView(globalIndex, cell.View);
                viewLayout.ParentView = ListView.ContentView;
                var size = ListView.Orientation == Orientation.Horizontal
                    ? new Vector2(SizeCache[index], Size)
                    : new Vector2(Size, SizeCache[index]);

                cell.Update(viewLayout, index, GetItemPosition(index), size);
                Items.AddLast(cell);
            }
        }

        public void Reposition(float delta)
        {
            UpdateVisibleItemCount(StartIndex);
            if (delta > 0)
            {
                LinkedListNode<FlowAdapterCellHolder> node = Items.First;
                // 获取在 StartIndex 之前的元素数量，并将其回收
                int count = 0;
                while (node != null)
                {
                    if (node.Value.Index >= StartIndex) break;
                    count++;
                    node = node.Next;
                }

                for (int i = 0; i < count; i++)
                {
                    FlowAdapterCellHolder item = Items.First.Value;
                    ListView.Caches[item.Type].Enqueue(item);
                    Items.RemoveFirst();
                }

                count = Items.Count;
                int missing = VisibleItemCount - count;
                for (int i = 0; i < missing; i++)
                {
                    int index = Items.Count == 0 ? 0 : Items.Last.Value.Index + 1;
                    int globalIndex = IndexCache[index];

                    FlowAdapterCellHolder cellHolder = ListView.Caches[ListView.TypeCache[globalIndex]].Dequeue();
                    cellHolder.Type = ListView.TypeCache[globalIndex];
                    IViewLayout viewLayout = ListView.Adapter.GetConvertView(globalIndex, cellHolder.View);
                    viewLayout.ParentView = ListView.ContentView;
                    var size = ListView.Orientation == Orientation.Horizontal
                        ? new Vector2(SizeCache[index], Size)
                        : new Vector2(Size, SizeCache[index]);
                    cellHolder.Update(viewLayout, index, GetItemPosition(index), size);
                    Items.AddLast(cellHolder);
                }
            }
            else
            {
                LinkedListNode<FlowAdapterCellHolder> node = Items.Last;
                int count = 0;
                while (node != null)
                {
                    if (node.Value.Index < StartIndex + VisibleItemCount) break;
                    count++;
                    node = node.Previous;
                }

                for (int i = 0; i < count; i++)
                {
                    FlowAdapterCellHolder item = Items.Last.Value;
                    ListView.Caches[item.Type].Enqueue(item);
                    Items.RemoveLast();
                }

                count = Items.Count;
                int missing = VisibleItemCount - count;
                for (int i = 0; i < missing; i++)
                {
                    int index = Items.Count == 0 ? 0 : Items.First.Value.Index - 1;
                    int globalIndex = IndexCache[index];

                    FlowAdapterCellHolder cellHolder = ListView.Caches[ListView.TypeCache[globalIndex]].Dequeue();
                    cellHolder.Type = ListView.TypeCache[globalIndex];
                    IViewLayout viewLayout = ListView.Adapter.GetConvertView(globalIndex, cellHolder.View);
                    viewLayout.ParentView = ListView.ContentView;
                    var size = ListView.Orientation == Orientation.Horizontal
                        ? new Vector2(SizeCache[index], Size)
                        : new Vector2(Size, SizeCache[index]);
                    cellHolder.Update(viewLayout, index, GetItemPosition(index), size);
                    Items.AddFirst(cellHolder);
                }
            }
        }

        public Vector2 GetItemPosition(int index)
        {
            return new Vector2(AnchorOffset, -AnchorCache[index]) - new Vector2(0, ListView.HeaderViewSize);
        }

        public int GetItemIndex(float position)
        {
            int target = Algorithm.BinarySearchFloat(AnchorCache, position);
            if (target >= 0)
            {
                if (position > AnchorCache[target] + SizeCache[target]) return -1;
            }

            return IndexCache[target];
        }
    }

    public class FlowAdapterCellHolder : AdapterCellHolder
    {
        public int Type;
    }
}